// -------------------------------------------------
// BlinKit - BlinKit Library
// -------------------------------------------------
//   File Name: ObjectPainter.cpp
// Description: ObjectPainter Class
//      Author: Ziming Li
//     Created: 2021-08-17
// -------------------------------------------------
// Copyright (C) 2021 MingYang Software Technology.
// -------------------------------------------------

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "./ObjectPainter.h"

#include "blinkit/blink/renderer/core/layout/LayoutBlock.h"
#include "blinkit/blink/renderer/core/layout/LayoutInline.h"
#include "blinkit/blink/renderer/core/layout/LayoutObject.h"
#include "blinkit/blink/renderer/core/layout/layout_theme.h"
#include "blinkit/blink/renderer/core/paint/BoxBorderPainter.h"
#include "blinkit/blink/renderer/core/paint/LayoutObjectDrawingRecorder.h"
#include "blinkit/blink/renderer/core/paint/PaintInfo.h"
#include "blinkit/blink/renderer/core/style/BorderEdge.h"
#include "blinkit/blink/renderer/core/style/ComputedStyle.h"
#include "blinkit/blink/renderer/platform/geometry/LayoutPoint.h"

namespace blink {

namespace {

void paintSingleRectangleOutline(const PaintInfo& paintInfo, const IntRect& rect, const ComputedStyle& style, const Color& color)
{
    ASSERT(!style.outlineStyleIsAuto());

    LayoutRect inner(rect);
    inner.inflate(style.outlineOffset());
    LayoutRect outer(inner);
    outer.inflate(style.outlineWidth());
    const BorderEdge commonEdgeInfo(style.outlineWidth(), color, style.outlineStyle());
    BoxBorderPainter(style, outer, inner, commonEdgeInfo).paintBorder(paintInfo, outer);
}

struct OutlineEdgeInfo {
    int x1;
    int y1;
    int x2;
    int y2;
    BoxSide side;
};

// Adjust length of edges if needed. Returns the width of the joint.
int adjustJoint(int outlineWidth, OutlineEdgeInfo& edge1, OutlineEdgeInfo& edge2)
{
    // A clockwise joint:
    // - needs no adjustment of edge length because our edges are along the clockwise outer edge of the outline;
    // - needs a positive adjacent joint width (required by drawLineForBoxSide).
    // A counterclockwise joint:
    // - needs to increase the edge length to include the joint;
    // - needs a negative adjacent joint width (required by drawLineForBoxSide).
    switch (edge1.side) {
    case BSTop:
        switch (edge2.side) {
        case BSRight: // Clockwise
            return outlineWidth;
        case BSLeft: // Counterclockwise
            edge1.x2 += outlineWidth;
            edge2.y2 += outlineWidth;
            return -outlineWidth;
        default: // Same side or no joint.
            return 0;
        }
    case BSRight:
        switch (edge2.side) {
        case BSBottom: // Clockwise
            return outlineWidth;
        case BSTop: // Counterclockwise
            edge1.y2 += outlineWidth;
            edge2.x1 -= outlineWidth;
            return -outlineWidth;
        default: // Same side or no joint.
            return 0;
        }
    case BSBottom:
        switch (edge2.side) {
        case BSLeft: // Clockwise
            return outlineWidth;
        case BSRight: // Counterclockwise
            edge1.x1 -= outlineWidth;
            edge2.y1 -= outlineWidth;
            return -outlineWidth;
        default: // Same side or no joint.
            return 0;
        }
    case BSLeft:
        switch (edge2.side) {
        case BSTop: // Clockwise
            return outlineWidth;
        case BSBottom: // Counterclockwise
            edge1.y1 -= outlineWidth;
            edge2.x2 += outlineWidth;
            return -outlineWidth;
        default: // Same side or no joint.
            return 0;
        }
    default:
        ASSERT_NOT_REACHED();
        return 0;
    }
}

void paintComplexOutline(GraphicsContext& graphicsContext, const Vector<IntRect> rects, const ComputedStyle& style, const Color& color)
{
    ASSERT(!style.outlineStyleIsAuto());

    // Construct a clockwise path along the outer edge of the outline.
    SkRegion region;
    int width = style.outlineWidth();
    int outset = style.outlineOffset() + style.outlineWidth();
    for (auto& r : rects) {
        IntRect rect = r;
        rect.inflate(outset);
        region.op(rect, SkRegion::kUnion_Op);
    }
    SkPath path;
    if (!region.getBoundaryPath(&path))
        return;

    Vector<OutlineEdgeInfo, 4> edges;

    SkPath::Iter iter(path, false);
    SkPoint points[4];
    size_t count = 0;
    for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb; verb = iter.next(points, false)) {
        if (verb != SkPath::kLine_Verb)
            continue;

        edges.grow(++count);
        OutlineEdgeInfo& edge = edges.last();
        edge.x1 = SkScalarTruncToInt(points[0].x());
        edge.y1 = SkScalarTruncToInt(points[0].y());
        edge.x2 = SkScalarTruncToInt(points[1].x());
        edge.y2 = SkScalarTruncToInt(points[1].y());
        if (edge.x1 == edge.x2) {
            if (edge.y1 < edge.y2) {
                edge.x1 -= width;
                edge.side = BSRight;
            } else {
                std::swap(edge.y1, edge.y2);
                edge.x2 += width;
                edge.side = BSLeft;
            }
        } else {
            ASSERT(edge.y1 == edge.y2);
            if (edge.x1 < edge.x2) {
                edge.y2 += width;
                edge.side = BSTop;
            } else {
                std::swap(edge.x1, edge.x2);
                edge.y1 -= width;
                edge.side = BSBottom;
            }
        }
    }

    if (!count)
        return;

    Color outlineColor = color;
    bool useTransparencyLayer = color.hasAlpha();
    if (useTransparencyLayer) {
        graphicsContext.beginLayer(static_cast<float>(color.alpha()) / 255);
        outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
    }

    ASSERT(count >= 4 && edges.size() == count);
    int firstAdjacentWidth = adjustJoint(width, edges.last(), edges.first());

    // The width of the angled part of starting and ending joint of the current edge.
    int adjacentWidthStart = firstAdjacentWidth;
    int adjacentWidthEnd;
    for (size_t i = 0; i < count; ++i) {
        OutlineEdgeInfo& edge = edges[i];
        adjacentWidthEnd = i == count - 1 ? firstAdjacentWidth : adjustJoint(width, edge, edges[i + 1]);
        int adjacentWidth1 = adjacentWidthStart;
        int adjacentWidth2 = adjacentWidthEnd;
        if (edge.side == BSLeft || edge.side == BSBottom)
            std::swap(adjacentWidth1, adjacentWidth2);
        ObjectPainter::drawLineForBoxSide(graphicsContext, edge.x1, edge.y1, edge.x2, edge.y2, edge.side, outlineColor, style.outlineStyle(), adjacentWidth1, adjacentWidth2, false);
        adjacentWidthStart = adjacentWidthEnd;
    }

    if (useTransparencyLayer)
        graphicsContext.endLayer();
}

} // namespace

void ObjectPainter::paintOutline(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    ASSERT(shouldPaintSelfOutline(paintInfo.phase));

    const ComputedStyle& styleToUse = m_layoutObject.styleRef();
    if (!styleToUse.hasOutline() || styleToUse.visibility() != VISIBLE)
        return;

    // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
    if (styleToUse.outlineStyleIsAuto() && !LayoutTheme::theme().shouldDrawDefaultFocusRing(m_layoutObject))
        return;

    Vector<LayoutRect> outlineRects;
    m_layoutObject.addOutlineRects(outlineRects, paintOffset, m_layoutObject.outlineRectsShouldIncludeBlockVisualOverflow());
    if (outlineRects.isEmpty())
        return;

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutObject, paintInfo.phase, paintOffset))
        return;

    // The result rects are in coordinates of m_layoutObject's border box.
    // Block flipping is not applied yet if !m_layoutObject.isBox().
    if (!m_layoutObject.isBox() && m_layoutObject.styleRef().isFlippedBlocksWritingMode()) {
        LayoutBlock* container = m_layoutObject.containingBlock();
        if (container) {
            m_layoutObject.localToAncestorRects(outlineRects, container, -paintOffset, paintOffset);
            if (outlineRects.isEmpty())
                return;
        }
    }

    Vector<IntRect> pixelSnappedOutlineRects;
    for (auto& r : outlineRects)
        pixelSnappedOutlineRects.append(pixelSnappedIntRect(r));

    IntRect unitedOutlineRect = unionRectEvenIfEmpty(pixelSnappedOutlineRects);
    IntRect bounds = unitedOutlineRect;
    bounds.inflate(m_layoutObject.styleRef().outlineOutsetExtent());
    LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutObject, paintInfo.phase, bounds, paintOffset);

    Color color = m_layoutObject.resolveColor(styleToUse, CSSPropertyOutlineColor);
    if (styleToUse.outlineStyleIsAuto()) {
        paintInfo.context.drawFocusRing(pixelSnappedOutlineRects, styleToUse.outlineWidth(), styleToUse.outlineOffset(), color);
        return;
    }

    if (unitedOutlineRect == pixelSnappedOutlineRects[0]) {
        paintSingleRectangleOutline(paintInfo, unitedOutlineRect, styleToUse, color);
        return;
    }
    paintComplexOutline(paintInfo.context, pixelSnappedOutlineRects, styleToUse, color);
}

void ObjectPainter::paintInlineChildrenOutlines(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    ASSERT(shouldPaintDescendantOutlines(paintInfo.phase));

    PaintInfo paintInfoForDescendants = paintInfo.forDescendants();
    for (LayoutObject* child = m_layoutObject.slowFirstChild(); child; child = child->nextSibling()) {
        if (child->isLayoutInline() && !toLayoutInline(child)->hasSelfPaintingLayer())
            child->paint(paintInfoForDescendants, paintOffset);
    }
}

void ObjectPainter::addPDFURLRectIfNeeded(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    ASSERT(false); // BKTODO:
#if 0
    ASSERT(paintInfo.isPrinting());
    if (m_layoutObject.isElementContinuation() || !m_layoutObject.node() || !m_layoutObject.node()->isLink() || m_layoutObject.styleRef().visibility() != VISIBLE)
        return;

    KURL url = toElement(m_layoutObject.node())->hrefURL();
    if (!url.isValid())
        return;

    Vector<LayoutRect> visualOverflowRects;
    m_layoutObject.addElementVisualOverflowRects(visualOverflowRects, paintOffset);
    IntRect rect = pixelSnappedIntRect(unionRect(visualOverflowRects));
    if (rect.isEmpty())
        return;

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutObject, DisplayItem::PrintedContentPDFURLRect, paintOffset))
        return;

    LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutObject, DisplayItem::PrintedContentPDFURLRect, rect, paintOffset);
    if (url.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(url, m_layoutObject.document().baseURL())) {
        String fragmentName = url.fragmentIdentifier();
        if (m_layoutObject.document().findAnchor(fragmentName))
            paintInfo.context.setURLFragmentForRect(fragmentName, rect);
        return;
    }
    paintInfo.context.setURLForRect(url, rect);
#endif
}

void ObjectPainter::drawLineForBoxSide(GraphicsContext& graphicsContext, int x1, int y1, int x2, int y2,
    BoxSide side, Color color, EBorderStyle style,
    int adjacentWidth1, int adjacentWidth2, bool antialias)
{
    int thickness;
    int length;
    if (side == BSTop || side == BSBottom) {
        thickness = y2 - y1;
        length = x2 - x1;
    } else {
        thickness = x2 - x1;
        length = y2 - y1;
    }

    // We would like this check to be an ASSERT as we don't want to draw empty borders. However
    // nothing guarantees that the following recursive calls to drawLineForBoxSide will have
    // positive thickness and length.
    if (length <= 0 || thickness <= 0)
        return;

    if (style == DOUBLE && thickness < 3)
        style = SOLID;

    switch (style) {
    case BNONE:
    case BHIDDEN:
        return;
    case DOTTED:
    case DASHED:
        drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side,
            color, thickness, style, antialias);
        break;
    case DOUBLE:
        drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color,
            thickness, adjacentWidth1, adjacentWidth2, antialias);
        break;
    case RIDGE:
    case GROOVE:
        drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color,
            style, adjacentWidth1, adjacentWidth2, antialias);
        break;
    case INSET:
        // FIXME: Maybe we should lighten the colors on one side like Firefox.
        // https://bugs.webkit.org/show_bug.cgi?id=58608
        if (side == BSTop || side == BSLeft)
            color = color.dark();
        // fall through
    case OUTSET:
        if (style == OUTSET && (side == BSBottom || side == BSRight))
            color = color.dark();
        // fall through
    case SOLID:
        drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, adjacentWidth1, adjacentWidth2, antialias);
        break;
    }
}

void ObjectPainter::drawDashedOrDottedBoxSide(GraphicsContext& graphicsContext, int x1, int y1, int x2, int y2,
    BoxSide side, Color color, int thickness, EBorderStyle style, bool antialias)
{
    ASSERT(thickness > 0);

    bool wasAntialiased = graphicsContext.shouldAntialias();
    StrokeStyle oldStrokeStyle = graphicsContext.strokeStyle();
    graphicsContext.setShouldAntialias(antialias);
    graphicsContext.setStrokeColor(color);
    graphicsContext.setStrokeThickness(thickness);
    graphicsContext.setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke);

    switch (side) {
    case BSBottom:
    case BSTop: {
        int midY = y1 + thickness / 2;
        graphicsContext.drawLine(IntPoint(x1, midY), IntPoint(x2, midY));
        break;
    }
    case BSRight:
    case BSLeft: {
        int midX = x1 + thickness / 2;
        graphicsContext.drawLine(IntPoint(midX, y1), IntPoint(midX, y2));
        break;
    }
    }
    graphicsContext.setShouldAntialias(wasAntialiased);
    graphicsContext.setStrokeStyle(oldStrokeStyle);
}

void ObjectPainter::drawDoubleBoxSide(GraphicsContext& graphicsContext, int x1, int y1, int x2, int y2,
    int length, BoxSide side, Color color, int thickness, int adjacentWidth1, int adjacentWidth2, bool antialias)
{
    int thirdOfThickness = (thickness + 1) / 3;
    ASSERT(thirdOfThickness > 0);

    if (!adjacentWidth1 && !adjacentWidth2) {
        StrokeStyle oldStrokeStyle = graphicsContext.strokeStyle();
        graphicsContext.setStrokeStyle(NoStroke);
        graphicsContext.setFillColor(color);

        bool wasAntialiased = graphicsContext.shouldAntialias();
        graphicsContext.setShouldAntialias(antialias);

        switch (side) {
        case BSTop:
        case BSBottom:
            graphicsContext.drawRect(IntRect(x1, y1, length, thirdOfThickness));
            graphicsContext.drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness));
            break;
        case BSLeft:
        case BSRight:
            graphicsContext.drawRect(IntRect(x1, y1, thirdOfThickness, length));
            graphicsContext.drawRect(IntRect(x2 - thirdOfThickness, y1, thirdOfThickness, length));
            break;
        }

        graphicsContext.setShouldAntialias(wasAntialiased);
        graphicsContext.setStrokeStyle(oldStrokeStyle);
        return;
    }

    int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3;
    int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3;

    switch (side) {
    case BSTop:
        drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
            y1, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness,
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
            y2 - thirdOfThickness, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y2,
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        break;
    case BSLeft:
        drawLineForBoxSide(graphicsContext, x1, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
            x1 + thirdOfThickness, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0),
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
            x2, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0),
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        break;
    case BSBottom:
        drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
            y1, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness,
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
            y2 - thirdOfThickness, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y2,
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        break;
    case BSRight:
        drawLineForBoxSide(graphicsContext, x1, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
            x1 + thirdOfThickness, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0),
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
            x2, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0),
            side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
        break;
    default:
        break;
    }
}

void ObjectPainter::drawRidgeOrGrooveBoxSide(GraphicsContext& graphicsContext, int x1, int y1, int x2, int y2,
    BoxSide side, Color color, EBorderStyle style, int adjacentWidth1, int adjacentWidth2, bool antialias)
{
    EBorderStyle s1;
    EBorderStyle s2;
    if (style == GROOVE) {
        s1 = INSET;
        s2 = OUTSET;
    } else {
        s1 = OUTSET;
        s2 = INSET;
    }

    int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2;
    int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2;

    switch (side) {
    case BSTop:
        drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1, 0) / 2, y1, x2 - std::max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2,
            side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias);
        drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(adjacentWidth2 + 1, 0) / 2, y2,
            side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
        break;
    case BSLeft:
        drawLineForBoxSide(graphicsContext, x1, y1 + std::max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(-adjacentWidth2, 0) / 2,
            side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias);
        drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(adjacentWidth2 + 1, 0) / 2,
            side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
        break;
    case BSBottom:
        drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1, 0) / 2, y1, x2 - std::max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2,
            side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias);
        drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(-adjacentWidth2 + 1, 0) / 2, y2,
            side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
        break;
    case BSRight:
        drawLineForBoxSide(graphicsContext, x1, y1 + std::max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(adjacentWidth2, 0) / 2,
            side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias);
        drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(-adjacentWidth2 + 1, 0) / 2,
            side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
        break;
    }
}

void ObjectPainter::drawSolidBoxSide(GraphicsContext& graphicsContext, int x1, int y1, int x2, int y2,
    BoxSide side, Color color, int adjacentWidth1, int adjacentWidth2, bool antialias)
{
    ASSERT(x2 >= x1);
    ASSERT(y2 >= y1);

    if (!adjacentWidth1 && !adjacentWidth2) {
        // Tweak antialiasing to match the behavior of fillPolygon();
        // this matters for rects in transformed contexts.
        bool wasAntialiased = graphicsContext.shouldAntialias();
        if (antialias != wasAntialiased)
            graphicsContext.setShouldAntialias(antialias);
        graphicsContext.fillRect(IntRect(x1, y1, x2 - x1, y2 - y1), color);
        if (antialias != wasAntialiased)
            graphicsContext.setShouldAntialias(wasAntialiased);
        return;
    }

    FloatPoint quad[4];
    switch (side) {
    case BSTop:
        quad[0] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y1);
        quad[1] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y2);
        quad[2] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y2);
        quad[3] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y1);
        break;
    case BSBottom:
        quad[0] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y1);
        quad[1] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y2);
        quad[2] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y2);
        quad[3] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y1);
        break;
    case BSLeft:
        quad[0] = FloatPoint(x1, y1 + std::max(-adjacentWidth1, 0));
        quad[1] = FloatPoint(x1, y2 - std::max(-adjacentWidth2, 0));
        quad[2] = FloatPoint(x2, y2 - std::max(adjacentWidth2, 0));
        quad[3] = FloatPoint(x2, y1 + std::max(adjacentWidth1, 0));
        break;
    case BSRight:
        quad[0] = FloatPoint(x1, y1 + std::max(adjacentWidth1, 0));
        quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0));
        quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0));
        quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0));
        break;
    }

    graphicsContext.fillPolygon(4, quad, color, antialias);
}

void ObjectPainter::paintAsPseudoStackingContext(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    // Pass PaintPhaseSelection and PaintPhaseTextClip to the descendants so that they will paint
    // for selection and text clip respectively. We don't need complete painting for these phases.
    if (paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip) {
        m_layoutObject.paint(paintInfo, paintOffset);
        return;
    }

    if (paintInfo.phase != PaintPhaseForeground)
        return;

    PaintInfo info(paintInfo);
    info.phase = PaintPhaseBlockBackground;
    m_layoutObject.paint(info, paintOffset);
    info.phase = PaintPhaseFloat;
    m_layoutObject.paint(info, paintOffset);
    info.phase = PaintPhaseForeground;
    m_layoutObject.paint(info, paintOffset);
    info.phase = PaintPhaseOutline;
    m_layoutObject.paint(info, paintOffset);
}

} // namespace blink
